﻿namespace EasyToolkit.Excel.Application.Async
{
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Threading;
    using EasyToolkit.Tools.Log;
    using Microsoft.Office.Interop.Excel;

    class ExcelNotifyPropertyManager : IDisposable
    {
        private volatile bool waitExcelBusy; 
        private readonly object syncObj = new object();
        private readonly BlockingCollection<NotityPropertyContext> contextItems;
        private readonly ExcelAsyncExecution excelAsyncExecution;
        private readonly Application Application;
        
        private readonly Thread thread;

        private bool isDisposed;

        #region .ctors
        public ExcelNotifyPropertyManager(ExcelApplication excelApplication)
        {
            contextItems = new BlockingCollection<NotityPropertyContext>();
            Application = excelApplication.Application;
            this.excelAsyncExecution = excelApplication.ExcelAsyncExecution;

            thread = new Thread(Execute);
            thread.Name = "NotifyPropertiesChanged";
            thread.IsBackground = true;
            thread.Start();

            excelAsyncExecution.ExcelDispatcher.ShutdownStarted += (s, o) => this.Dispose();
        }
        #endregion
        
        #region public methods
        public void Notify(NotityPropertyContext context)
        {
            if (isDisposed)
                return;

            if (contextItems.FirstOrDefault(i => i.ContextItem == context.ContextItem && ! i.ChangeColor) != null)
                return;
            else
                contextItems.Add(context);
        }

        public void Dispose()
        {
            try
            {
                isDisposed = true;
                thread.Abort();
            }
            catch
            {}
        }
        #endregion

        #region private methods
        private void NotifyChangeColor(NotityPropertyContext context)
        {
            if (isDisposed)
                return;

            if (contextItems.FirstOrDefault(i => i.ContextItem == context.ContextItem && context.ChangeColor) != null)
                return;
            else
                contextItems.Add(context);
        }


        private void Execute()
        {
            //long gap = 0;
            while (true)
            {
                if (waitExcelBusy)
                {
                    Thread.Sleep(75);
                    waitExcelBusy = false;
                    try
                    {
                        Application.EnableEvents = true;
                    }
                    catch { }
                }
                NotityPropertyContext context = contextItems.Take();
                excelAsyncExecution.ExcelDispatcher.BeginInvoke(new System.Action(() => ExecuteNotity(context)));
                //Thread.Sleep(1);  
            }
        }

        private void ExecuteNotity(NotityPropertyContext context)
        {
            if (isDisposed)
                return;

            if (context.ContextItem.IsDisposed || context.View.Renderer == null || context.View.Renderer.OutputRange == null)
                return;

            Worksheet worksheet = context.View.FirstOutputCell.Worksheet;
            Worksheet activeWorksheet = Application.ActiveSheet;
            Range range = null;
            bool enableEvent = Application.EnableEvents;
            try
            {
                KeyValuePair<int, int> kvp = context.Param;
                range = worksheet.Cells[context.View.FirstOutputCell.Row + kvp.Key, context.View.FirstOutputCell.Column + kvp.Value];
                if (context.ChangeColor)
                {
                    Application.EnableEvents = false;
                    range.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.OldLace);
                }
                else
                {
                    object value = context.ContextItem.ResolveBinding();
                    if (!object.Equals(range.Value2, value))
                    {
                        Application.EnableEvents = false;
                        range.Value2 = value;
                        //if (activeWorksheet == worksheet)
                        //{
                        //    object color = range.Interior.Color;
                        //    range.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.OrangeRed);
                        //    Task task = new Task(() =>
                        //    {
                        //        Thread.Sleep(333);
                        //        NotifyChangeColor(new NotityPropertContext(context.ContextItem, context.View, context.Param, true));
                        //    });
                        //    task.Start();
                        //}
                    }
                }
            }
            catch (COMException)
            {
                waitExcelBusy = true;
                Notify(context);
            }
            catch (Exception ex)
            {
                string message = string.Format("'ExecuteNotity' failed.{0}", ex.Message);
                Logger.Instance.LogException(LogType.Error, ex, message);
            }
            finally
            {
                try
                {
                    Application.EnableEvents = true;
                }
                catch
                { }
            }
            range = null;
            Marshal.ReleaseComObject(worksheet);
            Marshal.ReleaseComObject(activeWorksheet);
            worksheet = null;
            activeWorksheet = null;
        }
        #endregion
    }
}
